#include <netinet/in.h>
#include <stdlib.h>

#include <CUnit/CUnit.h>

#include "../pcm.h"

/* sample data taken from here: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
static unsigned char pcm_wav_example[72] = {
0x52, 0x49, 0x46, 0x46, 0x40, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 
0x22, 0x56, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x24, 0x17, 0x1e, 0xf3, 0x3c, 0x13, 0x3c, 0x14, 0x16, 0xf9, 0x18, 0xf9, 0x34, 0xe7, 0x23, 0xa6, 0x3c, 0xf2, 0x24, 0xf2, 0x11, 0xce, 0x1a, 0x0d
};

static void
test_sndfile_wav_file_is_correctly_read_from_memory()
{
	struct memory_io_ctx ctx;
	SF_VIRTUAL_IO vio;
	SF_INFO si;
	unsigned short frame[2];
	int fr;

	memset(&ctx, 0, sizeof(struct memory_io_ctx));
	ctx.stream_contents = pcm_wav_example;
	ctx.stream_size = 72;

	_init_ctx_sf_virtual_io(&vio);

	memset(&si, 0, sizeof(SF_INFO));
	SNDFILE *f = sf_open_virtual(&vio, SFM_READ, &si, &ctx);

	CU_ASSERT_EQUAL(si.samplerate, 22050);
	CU_ASSERT_EQUAL(si.channels, 2);
	CU_ASSERT_EQUAL(si.seekable, SF_TRUE);
	CU_ASSERT_EQUAL(si.frames, 7);

	fr = sf_readf_short(f, frame, 1);
	CU_ASSERT_EQUAL(fr, 1);
	CU_ASSERT_EQUAL(frame[0], 0);
	CU_ASSERT_EQUAL(frame[1], 0);

	fr = sf_readf_short(f, frame, 1);
	CU_ASSERT_EQUAL(fr, 1);

	CU_ASSERT_EQUAL(frame[0], ntohs(0x2417));
	CU_ASSERT_EQUAL(frame[1], ntohs(0x1ef3));

	/* cleaning up */
	sf_close(f);
}

static void
test_sndfile_wav_file_is_correctly_written_to_memory() /* we assume that the buffer is large enough */
{
	struct memory_io_ctx ctx;
	SF_VIRTUAL_IO vio;
	SF_INFO si;
	SNDFILE *f;
	short frame[2];

	memset(&ctx, 0, sizeof(struct memory_io_ctx));
	ctx.stream_size = 72;
	ctx.stream_contents = malloc(ctx.stream_size);

	_init_ctx_sf_virtual_io(&vio);

	memset(&si, 0, sizeof(SF_INFO));
	si.samplerate = 22050;
	si.channels = 2;
	si.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_FILE;

	f = sf_open_virtual(&vio, SFM_WRITE, &si, &ctx);

	frame[0] = 0;
	frame[1] = 0;
	sf_writef_short(f, frame, 1);

	frame[0] = ntohs(0x2417);
	frame[1] = ntohs(0x1ef3);
	sf_writef_short(f, frame, 1);

	frame[0] = ntohs(0x3c13);
	frame[1] = ntohs(0x3c14);
	sf_writef_short(f, frame, 1);

	frame[0] = ntohs(0x16f9);
	frame[1] = ntohs(0x18f9);
	sf_writef_short(f, frame, 1);

	frame[0] = ntohs(0x34e7);
	frame[1] = ntohs(0x23a6);
	sf_writef_short(f, frame, 1);

	frame[0] = ntohs(0x3cf2);
	frame[1] = ntohs(0x24f2);
	sf_writef_short(f, frame, 1);

	frame[0] = ntohs(0x11ce);
	frame[1] = ntohs(0x1a0d);
	sf_writef_short(f, frame, 1);

	sf_close(f);

	/* size_t i;
	for (i = 0; i < ctx.stream_size; ++i) {
		printf("0x%02x ", (unsigned char)ctx.stream_contents[i]);
	}
	printf("\n"); */

	CU_ASSERT_EQUAL(memcmp(ctx.stream_contents, pcm_wav_example, ctx.stream_size), 0);

	/* cleaning up */
	free(ctx.stream_contents);
}	

static void
test_ctx_flag_is_set_if_the_buffer_is_too_small()
{	
	struct memory_io_ctx ctx;
	SF_VIRTUAL_IO vio;
	SF_INFO si;
	SNDFILE *f;

	memset(&ctx, 0, sizeof(struct memory_io_ctx));
	ctx.stream_size = 2;
	ctx.stream_contents = malloc(ctx.stream_size);

	_init_ctx_sf_virtual_io(&vio);

	memset(&si, 0, sizeof(SF_INFO));
	si.samplerate = 22050;
	si.channels = 2;
	si.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;

	f = sf_open_virtual(&vio, SFM_WRITE, &si, &ctx);
	sf_close(f);

	free(ctx.stream_contents);

	CU_ASSERT_NOT_EQUAL(ctx.flags & MCTX_OUT_OF_BOUNDS, 0);
}

static void
test_pcm_decoder_gets_file_stats_correctly()
{
	struct mediafs_decoder_stat stat;
	struct mediafs_decoder *decoder;
	struct mediafs_decoder_ctx *ctx;
	int res;

	decoder = get_mediafs_pcm_decoder();
	ctx = mediafs_decoder_open(decoder, pcm_wav_example, sizeof(pcm_wav_example));
	res = mediafs_decoder_get_stat(ctx, &stat);

	CU_ASSERT_EQUAL(res, 0);
	CU_ASSERT_EQUAL(stat.len_samples, 7);
	CU_ASSERT_EQUAL(stat.samples_per_sec, 22050);
	CU_ASSERT_EQUAL(stat.bits_per_sample, 16);
	CU_ASSERT_EQUAL(stat.num_channels, 2);

	mediafs_decoder_close(ctx);
}

static void
test_pcm_decoder_reads_frames_correctly()
{
	unsigned short frames[6 * 2];
	struct mediafs_decoder *decoder;
	struct mediafs_decoder_ctx *ctx;
	int res;

	decoder = get_mediafs_pcm_decoder();
	ctx = mediafs_decoder_open(decoder, pcm_wav_example, sizeof(pcm_wav_example));
	res = mediafs_decoder_decode(ctx, 0, 6, (char *)frames);

	CU_ASSERT_EQUAL(res, 0);
	CU_ASSERT_EQUAL(frames[0], 0);
	CU_ASSERT_EQUAL(frames[1], 0);
	CU_ASSERT_EQUAL(frames[2], ntohs(0x2417));
	CU_ASSERT_EQUAL(frames[3], ntohs(0x1ef3));
	CU_ASSERT_EQUAL(frames[4], ntohs(0x3c13));
	CU_ASSERT_EQUAL(frames[5], ntohs(0x3c14));
	CU_ASSERT_EQUAL(frames[6], ntohs(0x16f9));
	CU_ASSERT_EQUAL(frames[7], ntohs(0x18f9));
	CU_ASSERT_EQUAL(frames[8], ntohs(0x34e7));
	CU_ASSERT_EQUAL(frames[9], ntohs(0x23a6));
	CU_ASSERT_EQUAL(frames[10], ntohs(0x3cf2));
	CU_ASSERT_EQUAL(frames[11], ntohs(0x24f2));

	mediafs_decoder_close(ctx);
}

static void
test_pcm_encoder_writes_header_correctly()
{
	char buffer[100];
	struct mediafs_decoder_stat decoder_stat;
	struct pcm_encoder_params params;
	struct mediafs_encoder *encoder;
	struct mediafs_encoder_ctx *ctx;
	size_t buffer_size;
	int res;

	decoder_stat.len_samples = 7;
	decoder_stat.samples_per_sec = 22050;
	decoder_stat.num_channels = 2;
	decoder_stat.bits_per_sample = 16;

	encoder = get_mediafs_pcm_encoder();
	fill_mediafs_pcm_encoder_params(&params, &decoder_stat);
	ctx = mediafs_encoder_open(encoder, (struct mediafs_encoder_params *)&params);

	buffer_size = sizeof(buffer);
	res = mediafs_encoder_write_header(ctx, buffer, &buffer_size);	
	CU_ASSERT_EQUAL(res, 0);
	CU_ASSERT_EQUAL(buffer_size, 44);
	CU_ASSERT_EQUAL(memcmp(buffer, pcm_wav_example, 44), 0);

	/* size_t i;
	for (i = 0; i < buffer_size; ++i) {
		printf("0x%02x ", (unsigned char)buffer[i]);
	}
	printf("\n"); */

	mediafs_encoder_close(ctx);
}

static void
test_pcm_encoder_encodes_frames_correctly()
{	
	unsigned char buffer[100];
	struct mediafs_decoder_stat decoder_stat;
	struct pcm_encoder_params params;
	struct mediafs_encoder *encoder;
	struct mediafs_encoder_ctx *ctx;
	size_t buffer_size;

	decoder_stat.len_samples = 7;
	decoder_stat.samples_per_sec = 22050;
	decoder_stat.num_channels = 2;
	decoder_stat.bits_per_sample = 16;

	encoder = get_mediafs_pcm_encoder();
	fill_mediafs_pcm_encoder_params(&params, &decoder_stat);
	ctx = mediafs_encoder_open(encoder, (struct mediafs_encoder_params *)&params);

	buffer_size = sizeof(buffer);
	mediafs_encoder_encode(ctx, 0, 1, pcm_wav_example + 48, buffer, &buffer_size);

	CU_ASSERT_EQUAL(buffer_size, 4);
	CU_ASSERT_EQUAL(buffer[0], 0x24);
	CU_ASSERT_EQUAL(buffer[1], 0x17);
	CU_ASSERT_EQUAL(buffer[2], 0x1e);
	CU_ASSERT_EQUAL(buffer[3], 0xf3);

/*	size_t i;
	for (i = 0; i < buffer_size; ++i) {
		printf("0x%02x ", (unsigned char)buffer[i]);
	}
	printf("\n");*/

	mediafs_encoder_close(ctx);

}

void
add_pcm_test_suite()
{
	CU_pSuite result;

	result = CU_add_suite("pcm_test", NULL, NULL);

	CU_ADD_TEST(result, test_sndfile_wav_file_is_correctly_read_from_memory);
	CU_ADD_TEST(result, test_sndfile_wav_file_is_correctly_written_to_memory);
	CU_ADD_TEST(result, test_ctx_flag_is_set_if_the_buffer_is_too_small);

	CU_ADD_TEST(result, test_pcm_decoder_gets_file_stats_correctly);
	CU_ADD_TEST(result, test_pcm_decoder_reads_frames_correctly);
	
	CU_ADD_TEST(result, test_pcm_encoder_writes_header_correctly);
	CU_ADD_TEST(result, test_pcm_encoder_encodes_frames_correctly);
}

